# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import svn.core

import gvn.changebranch
import gvn.cmdline
import gvn.errors
import gvn.util


helptext__gvn_snapshot = """snapshot: Snapshot a changebranch.
usage: snapshot [-c CHANGE]

Without options, snapshot all local changebranches.  Won't snapshot a
changebranch unless necessary; use gvn change to force it.
"""


def Check(cb, ctx):
  """Return {path: status} if cb needs snapshot, else None.

  Raises:
  gvn.errors.ChangeBranch     -- if any path in cb not under ctx.path

  """

  # We need a boolean indicating whether any cb path needs snapshot
  # because if any one path needs it, we need them all in paths.
  # Default based on --force option (or lack thereof).
  needs_snapshot = [ctx.options.force]
  paths = {}

  # Shouldn't the callback receive a pool, like most callbacks?  Fine,
  # we'll handle it ourselves.
  pool = svn.core.Pool(ctx.pool)
  def status_cb(target, status):
    pool.clear()
    target = ctx.wc.RelativePath(target)
    if not gvn.util.IsChild(target, ctx.path):
      raise gvn.errors.ChangeBranch("'%s' outside '%s'" % (target, ctx.path))
    if ctx.wc.NeedsSnapshot(target, status, pool):
      needs_snapshot[0] = True
    paths[target] = status
  ctx.wc.Status(list(cb.ChangedPathsRelative()),
                recursive=False, get_all=False, no_ignore=False,
                show_children=False, callback=status_cb)

  if needs_snapshot[0]:
    return paths
  return None


def Handle_GvnSnapshot(ctx):
  description = None
  if ctx.options.file is not None:
    description = open(ctx.options.file).read()
  elif ctx.options.message is not None:
    description = ctx.options.message
  # TODO(epg): force-log

  if ctx.options.change is None:
    ctx.wc.Open([ctx.path])
    iterpool = svn.core.Pool(ctx.pool)
    for i in set(x.change_name for x in ctx.wc.change_state.itervalues()):
      iterpool.clear()
      cb = gvn.changebranch.ChangeBranch(ctx.config, ctx.project, i)
      try:
        paths = Check(cb, ctx)
        if not paths:
          continue
      except gvn.errors.ChangeBranch, e:
        ctx.Notify('%s, skipping %s\n' % (e, cb.name))
        continue
      cb.Branch(ctx.wc, paths, description=description, pool=iterpool)
    iterpool.destroy()

  else:
    (username, cname, revision) = gvn.util.ParseChangeName(ctx.options.change)
    cb = gvn.changebranch.ChangeBranch(ctx.config, ctx.project,
                                       cname, username, revision)

    if description is None:
      description = cb.description

    ctx.wc.Open(list(cb.ChangedPathsRelative()))
    paths = Check(cb, ctx)
    if paths:
      cb.Branch(ctx.wc, paths, description=description, pool=ctx.pool)

  return 0

options = gvn.cmdline.AuthOptions(gvn.cmdline.LogOptions([
  'change',
  'force',
  'quiet'
]))
gvn.cmdline.AddCommand('snapshot', Handle_GvnSnapshot,
                       helptext__gvn_snapshot, options,
                       {'change': 'changebranch ARG'},
                       aliases=['snap'])
